/*
* Copyright 2011 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/
package org.etherframework.core.module
{
	import flash.events.EventPhase;
	import flash.utils.Dictionary;
	
	import mx.core.EventPriority;
	
	import org.etherframework.common.Access;
	import org.etherframework.common.IAccessable;
	import org.etherframework.common.IDestroyable;
	import org.etherframework.core.IModule;
	import org.etherframework.core.bridge.GlobalFacade;
	import org.etherframework.core.bridge.IBridgeFacades;
	import org.etherframework.core.bridge.IObjectController;
	import org.etherframework.core.context.Context;
	import org.etherframework.core.context.Default;
	import org.etherframework.core.events.GetParentModuleEvent;
	import org.etherframework.core.utils.AccessValidator;
	import org.etherframework.core.utils.Dispatcher;
	
	[ExcludeClass]
	public class ModuleContainer implements IModuleContainer, IDestroyable
	{
		private const _modules:Vector.<IModule> = new Vector.<IModule>();
		private const _controllers:Dictionary = new Dictionary(true);
		private var _dispatcher:Dispatcher;
		
		public function initialize(dispatcher:Dispatcher):void
		{
			if (!dispatcher)
				throw new ArgumentError("Argument dispatcher:Dispatcher can not be null.");
				
			_dispatcher = dispatcher;
			_dispatcher.listen(this, GetParentModuleEvent.GET_PARENT, 
				getParentHandler, false, EventPriority.CURSOR_MANAGEMENT);
		}
		
		public function addController(controller:IObjectController):void
		{
			if (!controller)
				throw new ArgumentError("Argument controller:IController can not be null");
			
			if (_modules.indexOf(controller.target) != -1)
				throw new ArgumentError("Controller for target " + controller.target + " is already added.");
			
			_modules.push(IModule(controller.target));
			_controllers[controller.target] = controller;
		}
		
		public function removeController(controller:IObjectController):void
		{
			if (!controller)
				throw new ArgumentError("Argument controller:IController can not be null");
			
			if (_modules.indexOf(controller.target) == -1)
				throw new ArgumentError("Controller for target " + controller.target + " was not added.");
			
			_modules.splice(_modules.indexOf(controller.target), 1);
			
			delete _controllers[controller.target];
		}
		
		public function hasController(module:IModule):IObjectController
		{
			return _controllers[module] as IObjectController;
		}
		
		public function destroy():void
		{
			_dispatcher.unlistenAll(this);
			
			if (_dispatcher is IDestroyable)
			{
				IDestroyable(_dispatcher).destroy();
			}
		}
		
		private function getParentHandler(e:Object):void
		{ 
			if (e.eventPhase == EventPhase.BUBBLING_PHASE)
			{
				// We need to stop it because ModuleContainer is the root
				// even in context of domain hierarchy.
				e.stopImmediatePropagation();
				const initiator:IBridgeFacades = e.initiator as IBridgeFacades;
				// We should make sure that interaction happens within domain 
				// or domain hierarchy but not related to standalone domain.
				if (!initiator)
					throw new Error("Interaction is not allowed. Probably your " +
						"module is loaded to separate ApplicationDomain.");
				
				// If module is not global do not assign parent as we finished
				// with facades chain resolving.
				if (initiator is IAccessable && 
					IAccessable(initiator).access == Access.GLOBAL)
				{
					e.parent = new GlobalFacade(_modules, new Default(null), 
						e.initiator.applicationDomain);
				}
			}
		}
	}
}